/***************************************************************************************************
  *Copyright(C),2010-2016,Sumscope
  *FileName	:  ProtoAdaptor.cpp
  *Author	:  scofined.qi
  *Version	:  1.0
  *Date		:  2019/01/17
  *Desc		:  //用于主要说明此程序文件完成的主要功能
  *Relation :
  *Others	:  //其他内容说明
  *Function :  //主要函数列表，每条记录应包含函数名及功能简要说明
  *History	:  //修改历史记录列表，每条修改记录应包含修改日期、修改者及修改内容简介
  ***************************************************************************************************/
#include "include/ProtoAdaptor.h"
#include "include/message.h"

namespace qb{
	namespace net{

		//ProtoList::ProtoList(const List* list) :m_list(list)
		//{

		//}

		ProtoList::ProtoList() : m_list(nullptr)
		{

		}

		ProtoList::ProtoList(const Variant& v)
		{
			v.RefList(m_list);
		}

		bool ProtoList::RefListFrom(const Message& msg, const char* name)
		{
			return msg.RefList(name, m_list) && m_list;
		}

		ProtoList::const_iterator ProtoList::begin() const
		{
			return m_list ? m_list->begin() : const_iterator();
		}

		ProtoList::const_iterator ProtoList::end() const
		{
			return m_list ? m_list->end() : const_iterator();
		}

		////////////////////////////////////////////////////////////////////////////////////
		ProtoMap::ProtoMap() :m_map(nullptr)
		{

		}

		//ProtoMap::ProtoMap(const VariantMap* map) : m_map(map)
		//{

		//}

		ProtoMap::ProtoMap(const Variant& v)
		{
			v.RefMap(m_map);
		}

		ProtoMap::~ProtoMap()
		{
			for (std::vector<ProtoList*>::iterator it = m_reflist.begin(); it != m_reflist.end(); ++it)
			{
				if (*it)	delete (*it);
			}
			m_reflist.clear();
		}

		bool ProtoMap::RefList(const char* name, const ProtoList*& pList) const
		{
			const qb::proto::VariantList* vlist = nullptr;
			if (m_map->RefList(name, vlist) && vlist)
			{
				ProtoList* temp = new ProtoList(vlist);
				(const_cast<ProtoMap*>(this))->m_reflist.push_back(temp);
				pList = temp;
				return true;
			}
			return false;
		}

		bool ProtoMap::RefMapFrom(const Message& msg, const char* name)
		{
			m_map = nullptr;
			return msg.RefMap(name, m_map) && m_map != nullptr;
		}

		bool ProtoMap::GetList(const char* name, ProtoList& list)const
		{
			const List* sublist = nullptr;
			m_map->RefList(name, sublist);
			list.m_list = sublist;
			return sublist != nullptr;
		}
		bool ProtoMap::getList(const char* name, ProtoList& list)const
		{
			return GetList(name, list);
		}

		bool ProtoMap::GetMap(const char* name, ProtoMap& map)const
		{
			const VariantMap* submap = nullptr;
			m_map->RefMap(name, submap);
			map.m_map = submap;
			return submap != nullptr;
		}
		ProtoMap::operator const ProtoMap::VariantMap&()const{
			return *m_map;
		}
		bool ProtoMap::RefString(const char *name, const char *& value, size_t &size) const
		{
			if (m_map && name)
				return m_map->RefString(name, value, size);
			return false;
		}
		/////////////////////////////////////////////////////////////////////////////

		ProtoMessage::ProtoMessage(const Message& msg) :m_message(&msg), m_map(nullptr)
		{

		}

		ProtoMessage::ProtoMessage(const ProtoMap& map) : m_map(map.m_map), m_message(nullptr)
		{

		}

		bool ProtoMessage::GetList(const char* name, ProtoList& list) const
		{
			if (m_message == nullptr && m_map == nullptr)
				return false;
			if (m_message)
				return list.RefListFrom(*m_message, name);
			const qb::proto::Message::List* sublist = nullptr;
			m_map->RefList(name, sublist);
			list.SetList(sublist);
			return sublist != nullptr;
		}

		const ProtoMap& ProtoMessage::MapValue()
		{
			const ProtoMap* m = new ProtoMap(m_message->MapValue());
			return *m;
		}
		bool ProtoMessage::GetRaw(const char *name, Raw& value) const
		{
			if (m_message == nullptr && m_map == nullptr)
				return false;
			return m_message?m_message->GetRaw(name, value):m_map->GetRaw(name, value);
		}
		bool ProtoMessage::getList(const char* name, ProtoList& list)const
		{
			if (m_message == nullptr && m_map == nullptr)
				return false;
			const qb::proto::Message::List* sublist = nullptr;
			if (m_message){
				m_message->RefList(name, sublist);
			}else
				m_map->RefList(name, sublist);
			list.SetList(sublist);
			return sublist != nullptr;
		}
		bool ProtoMessage::GetMap(const char* name, ProtoMap& map)const
		{
			if (m_message == nullptr && m_map == nullptr)
				return false;
			const qb::proto::Message::Map* submap = nullptr;
			if (m_message)
				m_message->RefMap(name, submap);
			else
				m_map->RefMap(name, submap);
			map.SetMap(submap); 
			return submap != nullptr;
		}
		bool ProtoMessage::RefString(const char *name, const char *& value, size_t &size) const
		{
			if (m_message == nullptr && m_map == nullptr)
				return false;
			if (m_message)
				return m_message->RefString(name, value, size);
			return m_map->RefString(name, value, size);
		}

	}// namespace net
}//namespace ss


namespace qb{
	namespace base{
		namespace msg{
			class Comparator{
				typedef qb::base::msg::Value Value;
				typedef qb::base::msg::ValueList ValueList;
				typedef qb::base::msg::ValueMap ValueMap;
				typedef ValueList::const_iterator const_iterator;
			public:
				static bool is_equal(const Value& v1, const qb::proto::Variant& v2)
				{
					// 对比值类型
					if (v1.Type != v2.Type)
						return false;
					bool equal = false;
					switch (v1.Type)
					{
					case Value::VAR_BOOL:equal = (bool)v1 == (bool)v2; break;
					case Value::VAR_INT8:equal = (qb::base::msg::int8_t)v1 == (qb::base::msg::int8_t)v2; break;
					case Value::VAR_UINT8:equal = (qb::base::msg::uint8_t)v1 == (qb::base::msg::uint8_t)v2; break;
					case Value::VAR_INT16:equal = (qb::base::msg::int16_t)v1 == (qb::base::msg::int16_t)v2; break;
					case Value::VAR_UINT16:equal = (qb::base::msg::uint16_t)v1 == (qb::base::msg::uint16_t)v2; break;
					case Value::VAR_INT32:equal = (qb::base::msg::int32_t)v1 == (qb::base::msg::int32_t)v2; break;
					case Value::VAR_UINT32:equal = (qb::base::msg::uint32_t)v1 == (qb::base::msg::uint32_t)v2; break;
					//case Value::VAR_INT64:equal = (qb::base::msg::int64_t)v1 == (qb::base::msg::int64_t)v2; break;
					//case Value::VAR_UINT64:equal = (qb::base::msg::uint64_t)v1 == (qb::base::msg::uint64_t)v2; break;
					case Value::VAR_FLOAT:equal = (float)v1 == (float)v2; break;
					case Value::VAR_DOUBLE:equal = (double)v1 == (double)v2; break;
					case Value::VAR_STRING:{
						std::string s2;
						v2.GetString(s2);
						int len = v1.size;
						if (v1.data && len > 0 && v1.data[len - 1] == 0)
							len = strlen(v1.data);
						std::string s1 = v1.data?std::string(v1.data, len):"";
						int len2 = s2.size();
						if (s2.c_str() && len2 > 0 && s2[len2 - 1] == 0)
						{
							len2 = strlen(s2.c_str());
							s2 = std::string(s2.c_str(), len2);
						}
						equal = s1 == s2;
					}break;
					case Value::VAR_INT64:{
								int64_t c1 = v1;
								int64_t c2 = v2.Data.i64;
								equal = c1 == c2;
							      }break;
					case Value::VAR_UINT64:{
                                                                uint64_t c1 = v1;
                                                                uint64_t c2 = v2.Data.u64;
                                                                equal = c1 == c2;
                                                              }break;
					case Value::VAR_RAW:{
						const void* val = nullptr; size_t len = 0;
						v2.RefRaw(val, len);
						if (v1.size == len && memcmp(val, v1.data, len) == 0)
							equal = true;
					}break;
					default:
						break;
					}
					return equal;
				}
				static bool is_equal(const ValueList& m1, const qb::proto::VariantList& m2)
				{
					// 对于list,逐个对比
					if (m1.size() != m2.size())
						return false;
					qb::proto::VariantList::const_iterator mit = m2.begin();
					for (const_iterator it = m1.begin(); it != m1.end(); ++it, ++mit)
					{
						const Value& v = *it;
						const qb::proto::Variant& pv = *mit;
						if (v.Type == Value::VAR_MAP)
						{
							ValueMap vmap(v);
							const qb::proto::VariantMap* map = nullptr;
							if (!pv.RefMap(map) || !is_equal(vmap, *map))
								return false;
						}
						else if (v.Type == Value::VAR_LIST)
						{
							ValueList vlist(v);
							const qb::proto::VariantList* list = nullptr;
							if (!pv.RefList(list) || !is_equal(vlist, *list))
								return false;
						}
						else {
							if (!is_equal(v, pv))
								return false;
						}
					}
					return true;
				}
				static bool is_equal(const ValueMap& m1, const qb::proto::VariantMap& m2)
				{
					if (m1.size() != m2.size())
						return false;
					qb::proto::VariantMap::const_iterator mit = m2.begin();
					for (const_iterator it = m1.begin(); it != m1.end() && mit != m2.end(); ++it, ++mit)
					{
						const Value& v = *it;
						std::string key(v.key, v.keylen);
						if (v.Type == Value::VAR_MAP)
						{
							ValueMap vmap(v);
							const qb::proto::VariantMap* pmap = nullptr;
							if (!m2.RefMap(key.c_str(), pmap) || !pmap)
								return false;
							if (!is_equal(vmap, *pmap))
								return false;
						}
						else if (v.Type == Value::VAR_LIST)
						{
							ValueList vlist(v);
							const qb::proto::VariantList* plist = nullptr;
							if (!m2.RefList(key.c_str(), plist) || !plist)
								return false;
							if (!is_equal(vlist, *plist))
								return false;
						}
						else
						{
							const qb::proto::Variant* pv = nullptr;
							Value vlast;//通过名称访问是找到最后一个
							if (!m1.find(key.c_str(), vlast))
								return false;
							if (!m2.getValue(key.c_str(), pv) || !is_equal(vlast, *pv))
								return false;
						}
					}
					return true;
				}
				static bool is_equal(const ProtoMessage& m1, const qb::proto::Message& m2)
				{
					if (m1.GetType() != m2.GetType())
						return false;
					if (m1.GetSubject() != m2.GetSubject())
						return false;
					if (m1.GetMessageID() != m2.GetMessageID())
						return false;

					return is_equal(m1.MapValue(), m2.MapValue());
				}
			};

			bool ProtoUtil::CheckMessage(const ProtoMessage& msg, const char* buf, int len)
			{
				static bool init = false;
				static bool enalbe = false;
				if (!init)
				{
					init = true;
				//	enalbe = qb::base::cfg::GetConfigBool(qb::base::kBaseConfigCheckProtoMessage); //syy
				}
				if (!enalbe)
					return true;
			//	qb::base::HighResTime timer;
				qb::proto::Message m2;
				int ret=m2.decode(buf, len);
			//	int last = timer.elapsed().microsec();
				if (ret <= 0)
				{
					return false;
				}
				bool equal = Comparator::is_equal(msg, m2);
			/*	if (!equal)
				{
					//将受到的消息写入到本地文件,便于后面测试分析
					static int msg_index = 0;
					equal = Comparator::is_equal(msg, m2);
					char fname[MAX_PATH] = { 0 };
					SYSTEMTIME st;
					GetLocalTime(&st);
					sprintf_s(fname, ".\\data\\message\\%s_%s_%02d%02d%02d_%2d%02d%2d_%3d_%d_%d_%d.bin",
						m2.GetMessageID().c_str(), m2.GetType().c_str(),
						st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds,
						ret, last, msg_index++
						);
					 qb::base::StringUtil::SaveFileContent(buf, len, fname); 
				}*///syy
				return equal;
			}
		}
	}
}

